#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
This module allows to select the virtual midi port to send to,
and then starts a bluetooth server over RFCOMM to get the input from the phone
"""

#TODOS:
#    make the message received 3 characters long, the first being a specific
#    commmand, could be "map X" for example, ok quit.

import sys
import thread
from optparse import OptionParser

try:
    import bluetooth as bt
except ImportError:
    raise Exception("You need the bluetooth library for python installed (pybluez)")

try:
    from rtmidi import RtMidiOut
    from rtmidi import RtError
except ImportError:
    raise Exception("You need the rtmidi library for python installed (rtmidi)")


#bluetooth uuid
UUID = "94f39d29-7d6d-437d-973b-fba39e49d4ee"

#midi messages' first and second value, the third is the actual value
MIDI_MESSAGE_X_1 = 0xb0
MIDI_MESSAGE_X_2 = 0x07
MIDI_MESSAGE_Y_1 = 0xb0
MIDI_MESSAGE_Y_2 = 0x02

def setupMidi(midiPort, mapa=True):
    """
    Opens a midi port for output,
    sends midi events for x and y axis to allow easy midi learn in host applications
    """
    print "================== MIDI SETUP =================="
    midiOut = RtMidiOut()
    if midiPort==None:
        nbPorts = midiOut.getPortCount()
        print "Number of virtual midi ports detected: %d"%nbPorts
        if nbPorts>0:
            print "midi ports available:"
            for i in xrange(nbPorts):
                print "[%d]\t%s"%(i,midiOut.getPortName(i))
            midiPort=-1
            while midiPort not in range(nbPorts):
                try:midiPort = int(raw_input("Enter the port number that you want to use "))
                #TODO:
                #except KeyboardInterrupt:
                #    sys.exit(0)
                except : midiPort=-1
            ##TODO?
            #try: midiOut.closePort()
            #except RtError, msg: pass
            midiOut.openPort(midiPort)
        else:
            raise Exception("No midi port available on your system, please install a virtual driver first.")
    else:
        midiOut.openPort(midiPort)
        print "opened midi port %s"%midiOut.getPortName(midiPort)
    if mapa:
        raw_input("[midi learn] -- press enter to send an X axis midi event to your software")
        midiOut.sendMessage(MIDI_MESSAGE_X_1, MIDI_MESSAGE_X_2, 0x01)
        raw_input("[midi learn] -- press enter to send an Y axis midi event to your software")
        midiOut.sendMessage(MIDI_MESSAGE_Y_1, MIDI_MESSAGE_Y_2, 0x01)
    else:
        print "skipping axis mapping part"
    print "============== MIDI SETUP FINISHED ============"
    return midiOut


def lauchBtServer(midiOut, portNum):
    """
    Launches a bluetooth server socket and waits for a client on port number
    as specified in the parameter 'portNum', then sends midi events through 'midiOut'
    """
    print "\n============= BLUETOOTH SERVER LAUNCH ===========\n"
    server_sock=bt.BluetoothSocket(bt.RFCOMM)
    server_sock.bind(("",bt.PORT_ANY))
    server_sock.listen(portNum)
    
    port = server_sock.getsockname()[1]
    
    bt.advertise_service(server_sock, "Server",
                         service_id = UUID,
                         service_classes = [ UUID, bt.SERIAL_PORT_CLASS ],
                         profiles = [ bt.SERIAL_PORT_PROFILE ])
    
    print "\nWaiting for connection on RFCOMM channel/port %d\n" % port
    client_sock, client_info = server_sock.accept()
    print "Phone connected: ", client_info
    
    try:
        while True:
            data = client_sock.recv(1024)
            if len(data) == 0: break
            else:
                x = ord(data[0])
                y = ord(data[1])
                #print "received x/y: %d/%d"%(x, y)
                midiOut.sendMessage(MIDI_MESSAGE_X_1, MIDI_MESSAGE_X_2, x)
                midiOut.sendMessage(MIDI_MESSAGE_Y_1, MIDI_MESSAGE_Y_2, y)
    except IOError:
        pass
    print "disconnected"
    
    client_sock.close()
    server_sock.close()
    print "all done"

if __name__=="__main__":
    usage = "usage: %prog [-m1] [-p1] [-s]"
    parser = OptionParser(usage=usage, version="%prog 1.0")
    parser.add_option("-m", "--midi-port", dest="midiPort", type="int",
                  help="Midi port number to use, if nothing is given, a list will be shown")
    parser.add_option("-s", "--skip-map", dest="mapa", action="store_false", default="True",
                      help="Skip the axis mapping part of the setup")
    parser.add_option("-p", "--port", dest="port", default=1, type="int",
                      help="Selects the port number the server will listen to")
    (options, args) = parser.parse_args()
    
    midiOut = setupMidi(options.midiPort, options.mapa)
    thread.start_new_thread(lauchBtServer, (midiOut, options.port))
    raw_input("\nPress enter to stop the server and quit")
    #TODO: terminate the bt server cleanly